/*
 * Created on Mar 12, 2004
 *
 * To change the template for this generated file go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
package org.vectrics.recruiter.user.resume;


import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Searcher;
import org.vectrics.SystemException;
import org.vectrics.common.util.DateSync;
import org.vectrics.common.util.FileUtil;
import org.vectrics.domain.Domain;
import org.vectrics.hibernate.ThreadProperties;
import org.vectrics.recruiter.RecruitServiceLocator;
import org.vectrics.recruiter.config.RecruitConfig;
import org.vectrics.recruiter.document.CandidateDocument;
import org.vectrics.recruiter.user.RecruitUserProvider;
import org.vectrics.recruiter.user.Resume;
import org.vectrics.recruiter.user.ResumeScanBatch;
import org.vectrics.recruiter.user.ResumeScanCriteria;
import org.vectrics.recruiter.user.ResumeScanResult;
import org.vectrics.user.Person;

//import com.opensymphony.util.FileUtils;



/**
 * @author mmoore
 *
 * Class for scanning resume content for key words
 */
public class ResumeScanner {
	private transient final Logger log = Logger.getLogger(this.getClass());
	private String indexPath = null;
	private ResumeScanBatch batch = null;
	
	/**
	 * 
	 */
	public ResumeScanner(ResumeScanBatch batch) {
		super();
		this.batch = batch;
    	indexPath = RecruitConfig.getRecruitInstance().getCandidateDocumentIndexPath(batch.getDomain());
	}
	
	public ResumeScanner(ResumeScanBatch batch, String path) {
		super();
		this.batch = batch;
		indexPath = path;
	}

	public ResumeScanner(String path) {
		super();
		indexPath = path;
	}

	public void scan(ResumeScanBatch resumeBatch) throws FileNotFoundException {
		if (log.isDebugEnabled()) {
			log.debug("Scanning resumes");
			log.debug(" - Index path = " + this.indexPath);
			log.debug(" - Batch  =     " + resumeBatch.getDescription());
			log.debug(" - Data Owner  =     " + resumeBatch.getDomain());
		}
		StringBuffer queryPhrase = new StringBuffer(200);
        Searcher searcher = null;
	    try {
            PorterStemAnalyzer analyzer = new PorterStemAnalyzer();
            
            FileUtil.createDirectoryIfNotExist(indexPath);
            try {
            	searcher = new IndexSearcher(indexPath);
            } catch (FileNotFoundException ex) {
            	log.warn("File not found exception - there may not be any documents current indexed" +
            			" - Index Path = " + indexPath +
            			" - Data Owner = " + resumeBatch.getDomain()
            			, ex);
            }
	        
            if (searcher != null) {
		        //String fieldName = "contents";
		    	Query query = null;
	        	Iterator iter = resumeBatch.getCriteriaList().iterator();
	        	if (iter.hasNext()) {
		        	while (iter.hasNext()) {
		        		ResumeScanCriteria criteria = (ResumeScanCriteria)iter.next();
		        		if (criteria.getWeight().intValue() < 10)
		        			throw new SystemException("Error - unexpected weight for user search phrase, less than 10: " + criteria.getWeight());
		        		String boostString = "^" + (criteria.getWeight().intValue()/10);
		        		
		        		queryPhrase.append("\""); 
			    		queryPhrase.append(criteria.getPhrase());
			    		queryPhrase.append("\"");
			    		queryPhrase.append(boostString);
			    		if (iter.hasNext())
			    			queryPhrase.append(" ");
				    	boolean required = false;  // OR conjunction
				    	boolean prohibited = false;
		        	}
		        	
		        	if (log.isDebugEnabled())
		        		log.debug("Query : " + queryPhrase.toString());
			        try {
			        	String queryStr = queryPhrase.toString();
				    	query = QueryParser.parse(queryStr, "contents", analyzer);
			        } catch (ParseException ex) {
			        	throw new SystemException("Exception processing parser, phrase = " + queryPhrase.toString(), ex);
			        }
			        
			        Date timeBeforeSearch = new Date();
			        Hits hits = searcher.search(query);
			        Date timeAfterSearch = new Date();
			        log.info("Hits: " + hits.length());
			        long millis = timeAfterSearch.getTime() - timeBeforeSearch.getTime();
			        log.info("Search time: " + millis);
			        
			        resumeBatch.setCriteriaList(resumeBatch.getCriteriaList());
			        RecruitServiceLocator.getRecruitUserService().clearResumeScanResults(resumeBatch);
			        Date timeBeforeSaveResults = new Date();
			        int maxScore = -1;
			        int i = 0;
			        boolean done = false;
			        while ((i < hits.length()) && (!done)) {
				        Document document = hits.doc(i);
				        float scoreRaw = hits.score(i);
				        int score = (int)(100f * scoreRaw);
				        if (maxScore < 0)
				        	maxScore = score;
				        if (isScoreLessThanMinimumCutoffPercent(resumeBatch, score, maxScore)) {
				        	if (i > resumeBatch.getMinimumCount().intValue()) {
					        	log.info("Minimum cutoff score reached, done scanning.");
					        	done = true;
				        	}
				        }
				        if (i > resumeBatch.getMaximumCount().intValue()) {
				        	log.info("Maximum scanning result count reached, done scanning.");
				        	done = true;
				        }
				        log.info("Score = " + score);
				        ThreadProperties.forcedCommit();
				        saveScanResult(resumeBatch, document, score);
				        ThreadProperties.forcedCommit();
				        i++;
			        }
			        Date timeAfterSaveResults = new Date();
			        millis = timeAfterSaveResults.getTime() - timeBeforeSaveResults.getTime();
			        if (log.isDebugEnabled())
			        	log.debug("Time to save results to Db: " + millis  + "  Result count = " + hits.length());
	        	} else {
	        		throw new SystemException("Attempted to run scan on batch that has no criteria");
	        	}
            } else {
            	log.info("Could not establish scanning - searcher object null, probably due to lack of indexed documents.");
            }
	    } catch (FileNotFoundException fnfe) {
	    	log.warn("Exception finding index files, index may not be built yet. Path = " + indexPath, fnfe);
	    } catch (IOException ioe) {
	    	throw new SystemException("IOException scanning documents", ioe);
	    } finally {
	    	try {
	    		if (searcher != null)
	    			searcher.close();
	    	} catch (Exception ex) {
	    		throw new SystemException("Exception closing searcher", ex);
	    	}
	    }
		
	}
	
    boolean isScoreLessThanMinimumCutoffPercent(ResumeScanBatch batch, int score, int maxScore) {
    	boolean result = false;
        int minPercCutoff = batch.getMinimumPercent().intValue();
        int minScore = (maxScore * minPercCutoff)/ 100;
        if (score < minScore)
        	result = true;
        return(result);
    }
	
	
    private void saveScanResult(ResumeScanBatch resumeBatch, Document document, int score) {
    	log.debug("Find candidate document");
		String documentId = document.getField("documentId").stringValue();
		try {
			CandidateDocument candidateDocument = RecruitServiceLocator.getRecruitUserService().findCandidateDocument(new Long(documentId));
		    ResumeScanResult result = new ResumeScanResult();
		    result.setBatch(resumeBatch);
		    result.setDocument(candidateDocument);
		    result.setPerson(candidateDocument.getPerson());
		    result.setRunDate(DateSync.getCurrentTime());
		    result.setScore(new Integer(score));
		    RecruitServiceLocator.getRecruitUserService().updateResumeScanResult(result);
		} catch (Exception ex) {
			log.warn("Exception saving result of document scan - continuing with next result", ex);
		}
    }
	
	
}
